package cn.edu.tju.elm.service.domain;

import cn.edu.tju.elm.model.wallet.Wallet;
import java.math.BigDecimal;

/**
 * 虚拟钱包领域对象 - 充血模型
 * 通过组合Wallet实体来实现业务逻辑
 */
public class VirtualWallet {
    private Wallet wallet;
    
    // ========== 验证方法 ==========
    
    /**
     * 验证金额为正数
     * @param amount 金额
     * @param fieldName 字段名称
     */
    private void validatePositiveAmount(BigDecimal amount, String fieldName) {
        if (amount == null) {
            throw new WalletDomainException(fieldName + "不能为空");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new WalletDomainException(fieldName + "必须大于0");
        }
    }

    /**
     * 验证金额为非负数
     * @param amount 金额
     * @param fieldName 字段名称
     */
    private void validateNonNegativeAmount(BigDecimal amount, String fieldName) {
        if (amount == null) {
            throw new WalletDomainException(fieldName + "不能为空");
        }
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new WalletDomainException(fieldName + "不能为负数");
        }
    }


    public VirtualWallet(Wallet wallet) {
        this.wallet = wallet;
    }

    /**
     * 获取当前可用余额（考虑冻结金额和透支额度）
     */
    public BigDecimal getAvailableBalance() {
        return wallet.getBalance()
            .subtract(wallet.getFrozenAmount())
            .add(wallet.getOverdraftLimit());
    }

    /**
     * 获取实际可用余额（不考虑透支）
     */
    public BigDecimal getActualAvailableBalance() {
        return wallet.getBalance().subtract(wallet.getFrozenAmount());
    }

    /**
     * 获取已使用透支额度
     */
    public BigDecimal getUsedOverdraft() {
        BigDecimal netBalance = wallet.getBalance().subtract(wallet.getFrozenAmount());
        return netBalance.compareTo(BigDecimal.ZERO) < 0 ? netBalance.abs() : BigDecimal.ZERO;
    }

    /**
     * 检查是否处于透支状态
     */
    public boolean isOverdraft() {
        return getUsedOverdraft().compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 检查是否允许透支
     */
    public boolean canOverdraft() {
        return wallet.getOverdraftLimit().compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 检查余额是否足够
     */
    public boolean hasSufficientBalance(BigDecimal amount) {
        return getAvailableBalance().compareTo(amount) >= 0;
    }

    // ========== 业务操作方法 ==========

    /**
     * 充值操作，优先归还透支，剩余金额再增加余额和奖励金额
     * @param amount 充值金额
     * @param reward 奖励金额
     * @return 返回实际归还的透支金额
     */
    public BigDecimal recharge(BigDecimal amount, BigDecimal reward) {
        validatePositiveAmount(amount, "充值金额");
        validateNonNegativeAmount(reward, "奖励金额");
        
        // 先记录当前的透支金额（在增加余额之前）
        BigDecimal usedOverdraft = getUsedOverdraft();
        
        // 增加充值金额和奖励金额到余额
        wallet.setBalance(wallet.getBalance().add(amount).add(reward));
        wallet.setRewardAmount(wallet.getRewardAmount().add(reward));
        
        // 如果之前有透支，优先使用奖励金来"归还"（从奖励金中扣除相应金额）
        if (usedOverdraft.compareTo(BigDecimal.ZERO) > 0) {
            // 优先从奖励金中扣除用于归还透支的部分
            BigDecimal rewardToUse = wallet.getRewardAmount().min(usedOverdraft);
            wallet.setRewardAmount(wallet.getRewardAmount().subtract(rewardToUse));
        }
        
        return usedOverdraft;
    }

    /**
     * 提现操作 - 优先扣除奖励金额
     * @param amount 提现金额
     * @param fee 手续费
     */
    public void withdraw(BigDecimal amount, BigDecimal fee) {
        validatePositiveAmount(amount, "提现金额");
        validateNonNegativeAmount(fee, "手续费");
        
        BigDecimal totalDeduct = amount.add(fee);
        
        if (!hasSufficientBalance(totalDeduct)) {
            throw new WalletDomainException("余额不足，当前可用余额：" + getAvailableBalance());
        }
        
        // 优先扣除奖励金额
        BigDecimal rewardToDeduct = wallet.getRewardAmount().min(totalDeduct);
        wallet.setRewardAmount(wallet.getRewardAmount().subtract(rewardToDeduct));
        
        // 从总余额中扣除全部金额（包括从奖励金扣除的部分）
        wallet.setBalance(wallet.getBalance().subtract(totalDeduct));
    }

    /**
     * 支付操作 - 优先使用奖励金额，余额不足时可以使用透支额度
     * @param amount 支付金额
     * @param allowOverdraft 是否允许使用透支额度
     * @return 返回实际使用的透支金额（如果使用了透支）
     */
    public BigDecimal pay(BigDecimal amount, boolean allowOverdraft) {
        validatePositiveAmount(amount, "支付金额");
        
        // 计算实际可用余额（不考虑透支）
        BigDecimal actualAvailable = getActualAvailableBalance();
        
        // 如果余额不足
        if (actualAvailable.compareTo(amount) < 0) {
            if (!allowOverdraft) {
                throw new WalletDomainException("余额不足，可用余额：" + actualAvailable);
            }
            
            // 检查是否可以使用透支额度
            BigDecimal availableOverdraft = getAvailableOverdraft();
            BigDecimal totalAvailable = actualAvailable.add(availableOverdraft);
            
            if (totalAvailable.compareTo(amount) < 0) {
                throw new WalletDomainException("余额和透支额度都不足，可用总额：" + totalAvailable);
            }
        }
        
        // 优先使用奖励金额支付（只是标记使用了奖励金，实际从balance扣除）
        BigDecimal rewardToUse = wallet.getRewardAmount().min(amount);
        wallet.setRewardAmount(wallet.getRewardAmount().subtract(rewardToUse));
        
        // 从balance中扣除支付金额（包括使用奖励金的部分）
        BigDecimal currentBalance = wallet.getBalance();
        BigDecimal overdraftUsed = BigDecimal.ZERO;
        
        if (currentBalance.compareTo(amount) >= 0) {
            // 余额充足，直接扣除
            wallet.setBalance(wallet.getBalance().subtract(amount));
        } else {
            // 余额不足，需要使用透支
            overdraftUsed = amount.subtract(currentBalance);
            wallet.setBalance(wallet.getBalance().subtract(amount));
        }
        
        return overdraftUsed;
    }
    
    /**
     * 支付操作（重载方法，默认不允许透支，保持向后兼容）
     * @param amount 支付金额
     */
    public void pay(BigDecimal amount) {
        pay(amount, false);
    }
    
    /**
     * 获取可用透支额度
     */
    public BigDecimal getAvailableOverdraft() {
        BigDecimal usedOverdraft = getUsedOverdraft();
        return wallet.getOverdraftLimit().subtract(usedOverdraft);
    }
    
    /**
     * 归还透支金额
     * @param amount 归还金额
     * @return 实际归还的金额
     */
    public BigDecimal repayOverdraft(BigDecimal amount) {
        validatePositiveAmount(amount, "归还金额");
        
        BigDecimal usedOverdraft = getUsedOverdraft();
        if (usedOverdraft.compareTo(BigDecimal.ZERO) <= 0) {
            // 没有透支，无需归还
            return BigDecimal.ZERO;
        }
        
        // 实际归还金额不能超过已使用的透支额度
        BigDecimal actualRepay = amount.min(usedOverdraft);
        
        // 增加余额来归还透支（余额为负时，增加余额相当于减少透支）
        wallet.setBalance(wallet.getBalance().add(actualRepay));
        
        return actualRepay;
    }

    /**
     * 转账到另一个钱包
     * @param targetWallet 目标钱包
     * @param amount 转账金额
     */
    public void transferTo(VirtualWallet targetWallet, BigDecimal amount) {
        if (targetWallet == null) {
            throw new WalletDomainException("目标钱包不能为空");
        }
        validatePositiveAmount(amount, "转账金额");
        
        if (this.wallet.getId().equals(targetWallet.getWallet().getId())) {
            throw new WalletDomainException("不能向自己转账");
        }
        
        if (!hasSufficientBalance(amount)) {
            throw new WalletDomainException("余额不足，无法完成转账");
        }
        
        // 从当前钱包扣除
        this.pay(amount);
        
        // 向目标钱包增加
        targetWallet.getWallet().setBalance(targetWallet.getWallet().getBalance().add(amount));
    }

    /**
     * 冻结金额操作
     * @param amount 冻结金额
     */
    public void freeze(BigDecimal amount) {
        validatePositiveAmount(amount, "冻结金额");
        
        if (getAvailableBalance().compareTo(amount) < 0) {
            throw new WalletDomainException("可用余额不足，无法冻结");
        }
        
        wallet.setBalance(wallet.getBalance().subtract(amount));
        wallet.setFrozenAmount(wallet.getFrozenAmount().add(amount));
    }

    /**
     * 入账冻结：商家侧在订单创建时预冻结待入账金额，不扣减余额
     */
    public void freezeIncome(BigDecimal amount) {
        validatePositiveAmount(amount, "入账冻结金额");
        if (wallet.getIncomingFrozenAmount() == null) {
            wallet.setIncomingFrozenAmount(BigDecimal.ZERO);
        }
        wallet.setIncomingFrozenAmount(wallet.getIncomingFrozenAmount().add(amount));
    }

    /**
     * 将入账冻结转为实际冻结（资金已到达但仍冻结）
     */
    public void realizeIncomeToFrozen(BigDecimal amount) {
        validatePositiveAmount(amount, "转为实际冻结金额");
        if (wallet.getIncomingFrozenAmount() == null || wallet.getIncomingFrozenAmount().compareTo(amount) < 0) {
            throw new WalletDomainException("入账冻结不足，无法转为实际冻结");
        }
        wallet.setIncomingFrozenAmount(wallet.getIncomingFrozenAmount().subtract(amount));
        wallet.setFrozenAmount(wallet.getFrozenAmount().add(amount));
    }

    /**
     * 解冻金额操作
     * @param amount 解冻金额
     */
    public void unfreeze(BigDecimal amount) {
        validatePositiveAmount(amount, "解冻金额");
        
        if (wallet.getFrozenAmount().compareTo(amount) < 0) {
            throw new WalletDomainException("冻结金额不足，无法解冻");
        }
        
        wallet.setBalance(wallet.getBalance().add(amount));
        wallet.setFrozenAmount(wallet.getFrozenAmount().subtract(amount));
    }

    /**
     * 退款操作，优先归还透支，剩余金额再退回到余额或奖励金额
     * @param amount 退款金额
     * @param isReward 是否退回到奖励金额
     * @return 返回实际归还的透支金额
     */
    public BigDecimal refund(BigDecimal amount, boolean isReward) {
        validatePositiveAmount(amount, "退款金额");
        
        // 先归还透支
        BigDecimal repayAmount = repayOverdraft(amount);
        
        // 剩余金额退回到余额或奖励金额
        BigDecimal remaining = amount.subtract(repayAmount);
        if (isReward) {
            wallet.setRewardAmount(wallet.getRewardAmount().add(remaining));
        } else {
            wallet.setBalance(wallet.getBalance().add(remaining));
        }
        
        return repayAmount;
    }

    /**
     * 透支操作(VIP用户专用)
     * @param amount 透支金额
     */
    public void overdraft(BigDecimal amount) {
        validatePositiveAmount(amount, "透支金额");
        
        if (!canOverdraft()) {
            throw new WalletDomainException("该钱包不允许透支");
        }
        
        if (amount.compareTo(wallet.getOverdraftLimit()) > 0) {
            throw new WalletDomainException("透支金额超过额度限制");
        }
        
        wallet.setBalance(wallet.getBalance().subtract(amount));
    }

    /**
     * 设置透支额度
     */
    public void setOverdraftLimit(BigDecimal overdraftLimit) {
        if (overdraftLimit == null || overdraftLimit.compareTo(BigDecimal.ZERO) < 0) {
            throw new WalletDomainException("透支额度不能为负数");
        }
        wallet.setOverdraftLimit(overdraftLimit);
    }

    /**
     * 增加透支额度
     */
    public void increaseOverdraftLimit(BigDecimal amount) {
        validatePositiveAmount(amount, "透支额度增加金额");
        wallet.setOverdraftLimit(wallet.getOverdraftLimit().add(amount));
    }

    /**
     * 减少透支额度
     */
    public void decreaseOverdraftLimit(BigDecimal amount) {
        validatePositiveAmount(amount, "透支额度减少金额");
        
        BigDecimal newLimit = wallet.getOverdraftLimit().subtract(amount);
        if (newLimit.compareTo(BigDecimal.ZERO) < 0) {
            throw new WalletDomainException("透支额度不能为负数");
        }
        
        wallet.setOverdraftLimit(newLimit);
    }

    // ========== Getter方法 ==========

    public Wallet getWallet() {
        return wallet;
    }

    public Long getId() {
        return wallet.getId();
    }

    public Long getUserId() {
        return wallet.getUserId();
    }

    public BigDecimal getBalance() {
        return wallet.getBalance();
    }

    public BigDecimal getFrozenAmount() {
        return wallet.getFrozenAmount();
    }

    public BigDecimal getOverdraftLimit() {
        return wallet.getOverdraftLimit();
    }

    public BigDecimal getRewardAmount() {
        return wallet.getRewardAmount();
    }
}

/**
 * 钱包领域异常
 */
class WalletDomainException extends RuntimeException {
    public WalletDomainException(String message) {
        super(message);
    }
    
    public WalletDomainException(String message, Throwable cause) {
        super(message, cause);
    }
}
